#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <cstdio>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>
#include <errno.h>
#include<cstring>
#include"InetAddress.h"
void setnonblock(int fd)
{
    int f1 = fcntl(fd, F_GETFL);
    if (f1 < 0)
    {
        std::cout << "没有任何标志位" << std::endl;
        exit(0);
    }
    fcntl(fd, F_SETFL, f1 | O_NONBLOCK);
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("usage: ./tcpepoll  port\n");
        printf("example: ./tcpepoll 8080\n\n");
        return -1;
    }

    // 下面两种写法都行
    // int listenSock = socket(AF_INET,SOCK_STREAM,0);
    // SOCK_NONBLOCK加这个选项可以编程非阻塞得
    int listenSock = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP);
    if (listenSock < 0)
    {
        perror("socket() failed");
        return -1;
    }

    int opt = 1;
    setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, &opt, static_cast<socklen_t>(sizeof opt)); // 必须的。
    setsockopt(listenSock, SOL_SOCKET, TCP_NODELAY, &opt, static_cast<socklen_t>(sizeof opt));  // 必须的。

    // setnonblock(listenSock); // 把服务端的listenfd设置为非阻塞的。

    // struct sockaddr_in local;
    // local.sin_family = AF_INET;
    // // 绑定“0.0.0.0”
    // local.sin_addr.s_addr = INADDR_ANY;
    // local.sin_port = htons(atoi(argv[1]));
    uint16_t port =atoi(argv[1]);
    InetAddress servaddr(port);
    if (bind(listenSock, servaddr.addr(), sizeof(servaddr.getSockAddr_in())) < 0)
    {
        perror("bind() failed");
        close(listenSock);
        return -1;
    }

    if (listen(listenSock, 128) != 0) // 在高并发的网络服务器中，第二个参数要大一些。
    {
        perror("listen() failed");
        close(listenSock);
        return -1;
    }

    int epollfd = epoll_create(1);
    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = listenSock;

    epoll_ctl(epollfd, EPOLL_CTL_ADD, listenSock, &ev);
    // 需要从队列中拿出数据 存放epoll_wait()返回事件的数组。
    struct epoll_event evs[10];

    // 最后一个式timeout = -1 等于阻塞
    //  = 0 立马返回，非阻塞
    //  大于0 就是隔几秒种看一下有事件wait没
    while (true)
    {
        int infds = epoll_wait(epollfd, evs, 10, -1);
        if (infds < 0)
        {
            perror("epoll_wait() failed");
            break;
        }

        // 超时。
        if (infds == 0)
        {
            printf("epoll_wait() timeout.\n");
            continue;
        }

        for (int i = 0; i < infds; i++)
        {
            if (evs[i].data.fd == listenSock)
            {
                InetAddress peer;
                socklen_t len = sizeof(peer.getSockAddr_in());
                // int acceptfd = accept(listenSock, (struct sockaddr *)&peer, &len); accept4 = accept +setnonblock
                int acceptfd = accept4(listenSock, peer.addr(), &len,SOCK_NONBLOCK);
                // setnonblock(acceptfd);
                printf("accept client(fd=%d,ip=%s,port=%d) ok.\n", acceptfd, peer.ip(), peer.port());

                struct epoll_event ev1;
                ev1.data.fd = acceptfd;
                ev1.events = EPOLLIN | EPOLLET;
                // 将事件添加到epoll里面
                epoll_ctl(epollfd, EPOLL_CTL_ADD, acceptfd, &ev1);
            }
            else
            {
                if (evs[i].events & EPOLLRDHUP) // 对方已关闭，有些系统检测不到，可以使用EPOLLIN，recv()返回0。
                {
                    printf("client(eventfd=%d) disconnected.\n", evs[i].data.fd);
                    close(evs[i].data.fd); // 关闭客户端的fd。
                }
                else if (evs[i].events & (EPOLLIN | EPOLLPRI)) // 接收缓冲区中有数据可以读。
                {
                    char buffer[1024];
                    while (true)
                    {
                        // 这里酒不用设置最后一个为0了
                        bzero(&buffer, sizeof(buffer));
                        // ssize_t read(int fd, void *buf, size_t count);
                        ssize_t nread = read(evs[i].data.fd, buffer, sizeof(buffer) - 1);
                        if (nread > 0)
                        {
                            buffer[nread] = 0;
                            printf("recv(eventfd=%d):%s\n", evs[i].data.fd, buffer);
                            send(evs[i].data.fd,buffer,strlen(buffer),0);
                        }
                        else if (nread == 0) // 是对方断开客户端了
                        {
                            printf("client(eventfd=%d) disconnected.\n", evs[i].data.fd);
                            close(evs[i].data.fd); // 关闭客户端的fd。
                            break;
                        }
                        else
                        {
                            if (errno == EINTR)
                            {
                                continue;
                            }
                            else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) // 没有错误 ，只是没有数据。
                            {
                                break;
                            }
                            else
                            {
                                // 真的出错了
                            }
                        }
                    }
                }
                else if (evs[i].events & EPOLLOUT)  // 有数据需要写，暂时没有代码，以后再说。
                {
                    // ...
                }
                else
                {
                    printf("client(eventfd=%d) error.\n", evs[i].data.fd);
                    close(evs[i].data.fd); // 关闭客户端的fd。
                }
            }
        }
    }
    return 0;
}